#include <stdlib.h>
#include <unistd.h>
#include "cmsis_os.h"
#include "log.h"

#if 1
#include "lvgl.h"
#include "monitor.h"
#include "mouse.h"
#include "keyboard.h"
#include "mousewheel.h"

// ##
void thread_lvgl(void const *pvParameter);
static void memory_monitor(lv_task_t *param) ;
#endif

#if 0

static mr_device_t lcd_device = MR_NULL;

static void flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    if (lcd_device)
    {
        mr_uint32_t *buf[3];
        buf[0] = (mr_uint32_t *)disp_drv;
        buf[1] = (mr_uint32_t *)area;
        buf[2] = (mr_uint32_t *)color_p;
        mr_device_write(lcd_device, -1, buf, sizeof(buf));
    }
}

mr_err_t mr_display_init(void)
{
    lcd_device = mr_device_find(MR_CFG_LCD_NAME);
    if(lcd_device == MR_NULL)
    {
        return -MR_ERR_NOT_FOUND;
    }

    monitor_init();

    return mr_device_open(lcd_device, MR_DEVICE_OFLAG_RDWR);
}
MR_INIT_DEVICE_EXPORT(mr_console_init);
#endif

/**
 * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics
 * library
 */
void display_drv_init(void)
{
#if 0
    mr_display_init();
#else
    monitor_init();
#endif

    /*Create a display buffer*/
    static lv_disp_buf_t disp_buf1;
    static lv_color_t buf1_1[LV_HOR_RES_MAX * 120];
    lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * 120);

    /*Create a display*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv); /*Basic initialization*/
    disp_drv.buffer = &disp_buf1;
#if 0
    disp_drv.flush_cb = flush_cb;
#else
    disp_drv.flush_cb = monitor_flush;
#endif
    lv_disp_drv_register(&disp_drv);

    /* Add the mouse as input device
    * Use the 'mouse' driver which reads the PC's mouse*/
    mouse_init();
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv); /*Basic initialization*/
    indev_drv.type = LV_INDEV_TYPE_POINTER;

    /*This function will be called periodically (by the library) to get the mouse position and state*/
    indev_drv.read_cb = mouse_read;
    lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv);

    /*Set a cursor for the mouse*/
    LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
    lv_obj_t * cursor_obj = lv_img_create(lv_scr_act(), NULL); /*Create an image object for the cursor */
    lv_img_set_src(cursor_obj, &mouse_cursor_icon);           /*Set the image source*/
    lv_indev_set_cursor(mouse_indev, cursor_obj);             /*Connect the image  object to the driver*/

    /*Add the keyboard as input device.*/
    lv_indev_drv_t kb_drv;
    lv_indev_drv_init(&kb_drv);
    kb_drv.type = LV_INDEV_TYPE_KEYPAD;
    kb_drv.read_cb = keyboard_read;
    lv_indev_drv_register(&kb_drv);

    /*Add the mousewheel as input device.*/
    lv_indev_drv_t enc_drv;
    lv_indev_drv_init(&enc_drv);
    enc_drv.type = LV_INDEV_TYPE_ENCODER;
    enc_drv.read_cb = mousewheel_read;
    lv_indev_drv_register(&enc_drv);

    /* Tick init.
    * You have to call 'lv_tick_inc()' in periodically to inform LittelvGL about
    * how much time were elapsed Create an SDL thread to do this*/
    // SDL_CreateThread(tick_thread, "tick", NULL);
    #define THREAD_LVGL_PRIORITY         osPriorityBelowNormal
    #define THREAD_LVGL_STACK_DEPTH      (configMINIMAL_STACK_SIZE)

    osThreadDef(thread_lvgl, "thread_lvgl", THREAD_LVGL_PRIORITY, 0, THREAD_LVGL_STACK_DEPTH);
    osThreadCreate(osThread(thread_lvgl), NULL);

    /* Optional:
    * Create a memory monitor task which prints the memory usage in
    * periodically.*/
    lv_task_create(memory_monitor, 5000, LV_TASK_PRIO_MID, NULL);
}

#if 1
// ##
#define LV_TIMER_INTERVAL_MS   (5)

// ##
void lv_timer_handler(void *argument)
{
    lv_tick_inc(LV_TIMER_INTERVAL_MS);
}

// ##
void thread_lvgl(void const *pvParameter)
{
    osTimerDef(lvgl_timer, lv_timer_handler);
    osTimerId_t lvgl_timer = osTimerCreate(osTimer(lvgl_timer), osTimerPeriodic, NULL);
    osTimerStart(lvgl_timer, LV_TIMER_INTERVAL_MS);
    log_info("%s starts to run\r\n", __func__);
    while(1)
    {
        lv_task_handler();
        osDelay(LV_TIMER_INTERVAL_MS);
    }
}

/**
 * Print the memory usage periodically
 * @param param
 */
static void memory_monitor(lv_task_t *param) {
  (void)param; /*Unused*/

  lv_mem_monitor_t mon;
  lv_mem_monitor(&mon);
  log_info("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d\n",
         (int)mon.total_size - mon.free_size, mon.used_pct, mon.frag_pct,
         (int)mon.free_biggest_size);
}
#endif